﻿using System;
using System.Collections.Generic;
//using Sys = System.Drawing;
using MyCraft.Minecraft_2D.Blocs;
using System.Diagnostics;
using System.Runtime.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;

namespace MyCraft.Minecraft_2D
{
    [DataContract]
    public class Map
    {
        #region Attributs;
        [DataMember]
        private static int _length, _height;
        [DataMember]
        private static List<List<Block>> _map;
        private static List<Point> _grottes;
        private static bool IsNew;
        private static int _previousHeight;
        #endregion

        #region Constructeurs
        public Map() : this(500, 256) { }

        public Map(Texture2D map, Texture2D light)
        {
            _length = map.Bounds.Width;
            _height = map.Bounds.Height;
            Color[] png = new Color[_length * _height];
            map.GetData<Color>(png);

            _map = new List<List<Block>>();
            Block.Map = this;

            for (int x = 0; x < _length; ++x)
            {
                List<Block> listX = new List<Block>();
                for (int y = 0; y < _height; ++y)
                {
                    TypeBloc type = TypeBloc.Air;
                    for (int i = 0; i < BlocSet.GetSingleton().Count; i++)
                    {
                        if (BlocSet.GetSingleton()[(TypeBloc)i]._color == png[x + y * _length])
                        {
                            type = (TypeBloc)i;
                            break;
                        }
                    }
                    Block b = new Block() { Bloc = BlocSet.GetSingleton()._newBlocs[type].Invoke(new Vector2(0.5f), x + y * _length) };
                    listX.Add(b);
                }
                _map.Add(listX);
            }

            for (int x = 0; x < _length; ++x)
            {
                for (int y = 0; y < _height; ++y)
                {
                    Block b = _map[x][y];
                    b.Position = new Point(x, y);
                    b.Top = (y > 0) ? _map[x][y - 1] : null;
                    b.Bottom = (y < _height-1) ? _map[x][y + 1] : null;
                    b.Left = (x > 0) ? _map[x - 1][y] : null;
                    b.Right = (x < _length-1) ? _map[x + 1][y] : null;
                }
            }

            //Gestion de la lumiere
            png = new Color[_length * _height];
            light.GetData<Color>(png);

            for (int x = 0; x < _length; ++x)
            {
                for (int y = 0; y < _height; ++y)
                {
                    Block b = _map[x][y];
                    b.IsSun = false;
                    b.Light = 100;
                    if (png[x + y * _length].A == byte.MinValue)
                        b.IsSun = true;
                    else
                        b.Light = (int)(-100 / 255f * (png[x + y * _length].A - 255));
                }
            }


            IsNew = false;
        }

        public Map(int length, int height)
        {
            Random rnd = new Random();
            IsNew = true;
            _length = length;
            _height = height;
            _previousHeight = rnd.Next(100, 150);
            _map = new List<List<Block>>();
            _grottes = new List<Point>();
            Block.Map = this;

            int blocks = BlocSet.GetSingleton().Percent;
            for (int x = 0; x < _length; ++x)
            {
                List<Block> listX = new List<Block>();
                for (int y = 0; y < _height; ++y)
                {
                    Block b = new Block() { Type = TypeBloc.Air };
                    listX.Add(b);
                }
                _map.Add(listX);
            }

            for (int x = 0; x < _length; ++x)
            {
                for (int y = 0; y < _height; ++y)
                {
                    _map[x][y].Position = new Point(x, y);
                    _map[x][y].Top = (y > 0) ? _map[x][y - 1] : null;
                    _map[x][y].Bottom = (y < _height-1) ? _map[x][y + 1] : null;
                    _map[x][y].Left = (x > 0) ? _map[x - 1][y] : null;
                    _map[x][y].Right = (x < _length-1) ? _map[x + 1][y] : null;
                }
            }
            for (int x = 0; x < _length; ++x)
            {
                List<int> listL = new List<int>();
                _previousHeight = rnd.Next(Math.Max(0, _previousHeight - 1), Math.Min(_height, _previousHeight + 2));
                for (int y = _previousHeight; y < _height - 1; ++y)
                {
                    int randBloc = rnd.Next(blocks);
                    bool trouve = false;
                    TypeBloc bloc;
                    Block b = _map[x][y];

                    //Gestion du pourcentage des blocs
                    int c = 0;
                    int p = 0;
                    while (!trouve && c < BlocSet.GetSingleton().Count)
                    {
                        p += BlocSet.GetSingleton()[(TypeBloc)c].Percent;
                        if (randBloc < p)
                            trouve = true;
                        else
                            c++;
                    }
                    randBloc = c;
                    bloc = (TypeBloc)randBloc;

                    bloc = BlocSet.GetSingleton()[bloc].OnNew(b);

                    //if (b.Top == null && b.Type == TypeBloc.Air)
                    //    b.IsSun = true;
                    //else if (b.Type == TypeBloc.Air)
                    //    b.IsSun = b.Top.IsSun;
                    //else
                    //    b.IsSun = false;

                    //Enregistrement de l'Id
                    if (!b.IsNew)
                        continue;
                    if (bloc != TypeBloc.None)
                    {
                        b.Bloc = BlocSet.GetSingleton()._newBlocs[bloc].Invoke(new Vector2(0.5f), x + y); ;
                    }
                    else
                        y--;
                }
            }

            //Gestion de la lumiere
            for (int x = 0; x < _length; x++)
            {
                for (int y = 0; y < _height; y++)
                {
                    Block b = _map[x][y];
                    //b.Refresh();
                    if (b.Top == null && b.Type == TypeBloc.Air)
                        b.IsSun = true;
                    else if (b.Type == TypeBloc.Air)
                        b.IsSun = b.Top.IsSun;
                    else
                        b.IsSun = false;
                    if (b.Top != null && !b.IsSun)
                        b.Light = b.Top.Light + b.Top.Bloc.Light;
                    else
                        b.Light = 100;
                }
            }

            IsNew = false;
            for (int x = 0; x < Length; x++)
            {
                _map[x][_height - 1].Type = TypeBloc.BedRock;
            }
        }
        #endregion

        #region Proprietes
        public int this[int length, int height]
        {
            get { return (int)_map[length][height].Type; }
            set
            {
                if(value != (int)TypeBloc.None)
                    _map[length][height].Type = (TypeBloc)value;
            }
        }

        public Block this[Point position]
        {
            get { return _map[position.X][position.Y]; }
            set
            {
                if (value.Type != TypeBloc.None)
                {
                    TypeBloc bloc = (IsNew) ?
                        BlocSet.GetSingleton()[value.Type].OnNew(_map[position.X][position.Y]) :
                            (BlocSet.GetSingleton()[value.Type].IsOK(_map[position.X][position.Y])) ?
                            value.Type :
                                TypeBloc.None;

                    if (bloc != TypeBloc.None)
                        _map[position.X][position.Y].Type = bloc;
                }
            }
        }

        public static int Length
        {
            get { return _length; }
        }

        public static int Height
        {
            get { return _height; }
        }
        #endregion

        #region Methodes
        public static int Up(int x)
        {
            if (x < 0 || x > Length)
                return -1;
            else
                for (int y = 0; y < Height; y++)
                    if (_map[x][y].Type != TypeBloc.Air)
                        return y - 1;
            return -1;
        }
        public static bool Exist(Block b)
        {
            return Exist(b.Position);
        }

        public static bool Exist(Point p)
        {
            return (p.X >= 0 && p.Y >= 0 && p.X < Map.Length && p.Y < Map.Height);
        }

        public static void Load(GraphicsDevice device)
        {
            Map m = new Map(Texture2D.FromStream(device,
                    File.OpenRead(@"Saves/Map.png")),
                Texture2D.FromStream(device,
                    File.OpenRead(@"Saves/Light.png")));
        }

        public static void Save(GraphicsDevice device)
        {
            //Sauvegarde de la Map
            Color[] png = new Color[Length * Height];
            for (int x = 0; x < Length; x++)
            {
                for (int y = 0; y < Height; y++)
                {
                    png[x + y * Length] = _map[x][y].Bloc.Color;
                }
            }
            Texture2D save = new Texture2D(device, Length, Height);
            save.SetData<Color>(png);
            FileStream fs = File.Open(@"Saves/Map.png", FileMode.OpenOrCreate);
            save.SaveAsPng(fs, Length, Height);
            fs.Close();

            //Sauvegarde des lumieres
            png = new Color[Length * Height];
            for (int x = 0; x < Length; x++)
            {
                for (int y = 0; y < Height; y++)
                {
                    Block b = _map[x][y];
                    png[x + y * Length].A = (b.IsSun) ? byte.MinValue : (byte)(255 - (int)(255 * (float)(b.Light / 100f)));
                }
            }
            save = new Texture2D(device, Length, Height);
            save.SetData<Color>(png);
            fs = File.Open(@"Saves/Light.png", FileMode.OpenOrCreate);
            save.SaveAsPng(fs, Length, Height);
            fs.Close();
        }

        public static IEnumerable<Block> InRange(Point p, int r, TypeBloc type)
        {
            while (r >= 0)
            {
                foreach (Block bloc in Circle(p, r--))
                    if (bloc.Type == type)
                        yield return bloc;
            }
            yield break; ;
        }

        public static IEnumerable<Block> Range(Point p, int r)
        {
            while (r >= 0)
            {
                foreach (Block bloc in Circle(p, r--))
                    yield return bloc;
            }
            yield break; ;
        }

        public static IEnumerable<Block> Circle(Point p, int r)
        {
            if (r < 0)
                yield break;

            int x = 0;
            int y = r;
            int d = r - 1;
            List<Block> list = new List<Block>();
            list.Add(_map[p.X][p.Y]);

            while (y >= x)
            {
                Point p2;
                p2 = new Point(x + p.X, y + p.Y);
                if (Exist(p2) && !list.Contains(_map[p2.X][p2.Y]))
                {
                    list.Add(_map[p2.X][p2.Y]);
                    yield return _map[p2.X][p2.Y];
                }

                p2 = new Point(y + p.X, x + p.Y);
                if (Exist(p2) && !list.Contains(_map[p2.X][p2.Y]))
                {
                    list.Add(_map[p2.X][p2.Y]);
                    yield return _map[p2.X][p2.Y];
                }

                p2 = new Point(-x + p.X, y + p.Y);
                if (Exist(p2) && !list.Contains(_map[p2.X][p2.Y]))
                {
                    list.Add(_map[p2.X][p2.Y]);
                    yield return _map[p2.X][p2.Y];
                }

                p2 = new Point(-y + p.X, x + p.Y);
                if (Exist(p2) && !list.Contains(_map[p2.X][p2.Y]))
                {
                    list.Add(_map[p2.X][p2.Y]);
                    yield return _map[p2.X][p2.Y];
                }

                p2 = new Point(x + p.X, -y + p.Y);
                if (Exist(p2) && !list.Contains(_map[p2.X][p2.Y]))
                {
                    list.Add(_map[p2.X][p2.Y]);
                    yield return _map[p2.X][p2.Y];
                }

                p2 = new Point(y + p.X, -x + p.Y);
                if (Exist(p2) && !list.Contains(_map[p2.X][p2.Y]))
                {
                    list.Add(_map[p2.X][p2.Y]);
                    yield return _map[p2.X][p2.Y];
                }

                p2 = new Point(-x + p.X, -y + p.Y);
                if (Exist(p2) && !list.Contains(_map[p2.X][p2.Y]))
                {
                    list.Add(_map[p2.X][p2.Y]);
                    yield return _map[p2.X][p2.Y];
                }

                p2 = new Point(-y + p.X, -x + p.Y);
                if (Exist(p2) && !list.Contains(_map[p2.X][p2.Y]))
                {
                    list.Add(_map[p2.X][p2.Y]);
                    yield return _map[p2.X][p2.Y];
                }


                if (d >= 2 * (x - 1))
                {
                    d -= 2 * x;
                    x++;
                }
                else if (d <= 2 * (r - y))
                {
                    d += 2 * y - 1;
                    y--;
                }
                else
                {
                    d += 2 * (y - x - 1);
                    y--;
                    x++;
                }
            }
            yield break;
        }
        #endregion
    }
}
